/*
 * Copyright (c) 2013, SRI International
 * All rights reserved.
 * Licensed under the The BSD 3-Clause License;
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 * 
 * http://opensource.org/licenses/BSD-3-Clause
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the aic-web-praise nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.sri.ai.lpitools.gwtgui.client.components.manager;

import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sri.ai.lpitools.cobra.gwtutil.dataobjects.shared.EvidenceInfo;
import com.sri.ai.lpitools.cobra.gwtutil.dataobjects.shared.ModelInfo;
//import com.sri.ai.lpitools.cobra.gwtutil.dataobjects.shared.ModelObject;
import com.sri.ai.lpitools.cobra.gwtutil.dataobjects.shared.QueryInfo;
import com.sri.ai.lpitools.cobra.gwtutil.dataobjects.shared.QueryOptionsGWT;
import com.sri.ai.lpitools.cobra.gwtutil.dataobjects.shared.UserInfo;
import com.sri.ai.lpitools.gwtgui.client.RPCService;
import com.sri.ai.lpitools.gwtgui.client.components.dialogs.ErrorDialogBox;
import com.sri.ai.lpitools.gwtgui.client.components.dialogs.MessageDialog;
import com.sri.ai.lpitools.gwtgui.client.components.dialogs.SaveAsDialog;
import com.sri.ai.lpitools.gwtgui.client.components.querypanel.EvidenceDataManager;
import com.sri.ai.lpitools.gwtgui.client.components.querypanel.QueryDataManager;
import com.sri.ai.lpitools.gwtgui.client.components.querypanel.QueryOptionsManager;
import com.sri.ai.lpitools.gwtgui.client.util.JSONUtil;

public class AICManager {
	private final UserManager userManager;
	private final ModelManager modelManager;
	private final QueryOptionsManager queryOptionsManager;
	private final ExecutionEngineDistributor executionEngineDistributor;
	private final QueryDataManager queryDataManager;
	private final EvidenceDataManager evidenceDataManager;
	private boolean runQueryLock = false;
	
	public AICManager(UserManager userManager, ModelManager modelManager, QueryDataManager qdm, EvidenceDataManager edm,  QueryOptionsManager qom, ExecutionEngineDistributor eed) {
		this.userManager = userManager;
		this.modelManager = modelManager;
		this.queryOptionsManager = qom;
		this.executionEngineDistributor = eed;
		this.queryDataManager = qdm;
		this.evidenceDataManager = edm;
	}
	
	public void runQuery() {
		if (!queryDataManager.getQueryText().equalsIgnoreCase("") && !modelManager.getCurrentText().equalsIgnoreCase("")){
			QueryOptionsGWT qogwt = queryOptionsManager.getQueryOptions();
			if(!runQueryLock){
				runQueryLock = true;
				RPCService.Util.getInstance().runQuery(
						userManager.getCurrentUser().getUser(),
						modelManager.getCurrentText(),
						evidenceDataManager.getEvidenceText(),	
						queryDataManager.getQueryText(),
						qogwt,
						new AsyncCallback<String>() {

							@Override
							public void onFailure(Throwable caught) {
								runQueryLock = false;
								MessageDialog.showMessageDialog("Error could not run query please seek help from admin " + caught.getMessage());

							}

							@Override
							public void onSuccess(String result) {
								runQueryLock = false;
								if(!result.equalsIgnoreCase("Error") && !result.startsWith("Got the following exception stack trace ")){
									JSONObject jsv = (JSONObject) JSONParser.parseStrict(result);	
									executionEngineDistributor.notifyListenersResultReceived(jsv);
								} else {
									if(result.startsWith("Got the following exception stack trace ")){
										ErrorDialogBox.showErrorDialog(result);
									} else{
										MessageDialog.showMessageDialog("Error in run query check server logs for detials");
									}
								}
							}
						});
			} else {
				MessageDialog.showMessageDialog("You are already executing a query, you can't execute one until this one is finished");
			}
		} else {
			if(queryDataManager.getQueryText().equalsIgnoreCase("")){
				MessageDialog.showMessageDialog("You can't execute an empty query");
			} 
			if(modelManager.getCurrentText().equalsIgnoreCase("")){
				MessageDialog.showMessageDialog("You can't execute a query against an empty model");
			}
		}
	}
	
	public void getLatexImage(String result) {
		final String queryResult = result;
		RPCService.Util.getInstance().getLatexImage(queryResult, 
				new AsyncCallback<String>() {

			@Override
			public void onFailure(Throwable caught) {
				executionEngineDistributor.notifyListenersTexLiveReceived(queryResult);
			}

			@Override
			public void onSuccess(String result) {
				if(!result.equalsIgnoreCase("Error")){	
					executionEngineDistributor.notifyListenersTexLiveReceived(result);
				} else {
					executionEngineDistributor.notifyListenersTexLiveReceived(queryResult);
				}
			}
		});
	}
	
	/**
	 * This functions call the RPC server and opens the model that matches the title passed in
	 */
	public static void openModel(final String modelName, final UserManager userManager, final ModelManager modelManager) {
		RPCService.Util.getInstance().getModel(userManager.getCurrentUser().getUser(), modelName, new AsyncCallback<String>() {

			@Override
			public void onFailure(Throwable caught) {
				MessageDialog.showMessageDialog("unable to get model for name " + modelName);
			}

			@Override
			public void onSuccess(String resultString) {
				if(resultString.startsWith("Error")) {
					//unable to load model
					MessageDialog.showMessageDialog("unable to get model for name " + modelName);
				} else {
					ModelInfo modelInfo = JSONUtil.getModelInfo(resultString);
					modelManager.setActiveModel(modelInfo, false);
				}
			}
		});
	}

	/**
	 * This function is used to save the selected model
	 */
	public static void saveModel(ModelManager modelManager, UserManager userManager){

		String currentText = modelManager.getCurrentText();
		if(currentText != null && modelManager.getActiveModel() != null) {
			modelManager.getActiveModel().setModelText(currentText);
			RPCService.Util.getInstance().saveModel(userManager.getCurrentUser().getUser(), modelManager.getActiveModel().getModelTitle(), modelManager.getActiveModel(),
					new AsyncCallback<Boolean>() {
				public void onSuccess(Boolean result) {
					//if the model is saved alert user that it has been saved
					//else alert user that it could not be saved
					if(result){
						MessageDialog.showMessageDialog("Model has been saved.");
					} else {
						String message = "Model could not be saved";					
						MessageDialog.showMessageDialog(message);
					}
				}
	
				public void onFailure(Throwable caught) {
					MessageDialog.showMessageDialog(caught.getMessage());
				}
	        });
		} else {
			ModelInfo model = modelManager.getActiveModel();
			SaveAsDialog.showLoginDialog(userManager, model , currentText, "Save As Prompt");
		}
	}

	/**
	 * This function is used to save the selected model
	 */
	public static void saveModelInfo(ModelManager modelManager, UserManager userManager){
		String currentText = modelManager.getCurrentText();
		if(currentText != null && modelManager.getActiveModel() != null) {
			modelManager.getActiveModel().setModelText(currentText);
			RPCService.Util.getInstance().saveModelInfo(userManager.getCurrentUser().getUser(), modelManager.getActiveModel().getModelTitle(), modelManager.getActiveModel(),
					new AsyncCallback<Boolean>() {
				public void onSuccess(Boolean result) {
					//if the model is saved alert user that it has been saved
					//else alert user that it could not be saved
					if(result){
						MessageDialog.showMessageDialog("Model has been saved.");
					} else {
						String message = "Model could not be saved.";					
						MessageDialog.showMessageDialog(message);
					}
				}
	
				public void onFailure(Throwable caught) {
					MessageDialog.showMessageDialog(caught.getMessage());
				}
	        });
		} else {
			ModelInfo model = modelManager.getActiveModel();
			SaveAsDialog.showLoginDialog(userManager, model , currentText, "Save As Prompt");
		}
	}	

	/**
	 * This function is used to save the selected model
	 */
	public static void saveModelBody(ModelManager modelManager, UserManager userManager){
		String currentText = modelManager.getCurrentText();
		if(currentText != null && modelManager.getActiveModel() != null) {
			modelManager.getActiveModel().setModelText(currentText);
			RPCService.Util.getInstance().saveModelBody(userManager.getCurrentUser().getUser(), modelManager.getActiveModel().getModelTitle(), modelManager.getActiveModel(),
					new AsyncCallback<Boolean>() {
				public void onSuccess(Boolean result) {
					//if the model is saved alert user that it has been saved
					//else alert user that it could not be saved
					if (result) {
						MessageDialog.showMessageDialog("Model has been saved.");
					} else {
						String message = "Model could not be saved.\n";					
						MessageDialog.showMessageDialog(message);
					}
				}
	
				public void onFailure(Throwable caught) {
					MessageDialog.showMessageDialog(caught.getMessage());
				}
	        });
		}
		else{
			ModelInfo model = modelManager.getActiveModel();
			SaveAsDialog.showLoginDialog(userManager, model , currentText, "Save As Prompt");
		}
	}	
	
	/**
	 * This function is used to save the selected model
	 */
	public static void saveModelBody(ModelInfo model, UserManager userManager){
		if(model != null) {
			RPCService.Util.getInstance().saveModelBody(userManager.getCurrentUser().getUser(), model.getModelTitle(), model, new AsyncCallback<Boolean>() {
				public void onSuccess(Boolean result) {
					//if the model is saved alert user that it has been saved
					//else alert user that it could not be saved
					if(result){
						MessageDialog.showMessageDialog("Model has been saved.");
					} else {
						String message = "Model could not be saved.\n";					
						MessageDialog.showMessageDialog(message);
					}
				}
	
				public void onFailure(Throwable caught) {
					MessageDialog.showMessageDialog(caught.getMessage());
				}
	        });
		}
	}	
	
	/**
	 * This function is used to save the selected model
	 */
	public static void saveModelInfo(ModelInfo model, UserManager userManager){
		if(model != null) {
			RPCService.Util.getInstance().saveModelInfo(userManager.getCurrentUser().getUser(), model.getModelTitle(), model, new AsyncCallback<Boolean>() {
				public void onSuccess(Boolean result) {
					//if the model is saved alert user that it has been saved
					//else alert user that it could not be saved
					if(result){
						MessageDialog.showMessageDialog("Model has been saved.");
					} else {
						String message = "Model could not be saved.\n";					
						MessageDialog.showMessageDialog(message);
					}
				}
	
				public void onFailure(Throwable caught) {
					MessageDialog.showMessageDialog(caught.getMessage());
				}
	        });
		}
	}	
	/**
	 * This function is used to save the selected model
	 */
	public static void saveModel(ModelInfo model, UserManager userManager){
		if(model != null) {
			RPCService.Util.getInstance().saveModel(userManager.getCurrentUser().getUser(), model.getModelTitle(), model, new AsyncCallback<Boolean>() {
				public void onSuccess(Boolean result) {
					//if the model is saved alert user that it has been saved
					//else alert user that it could not be saved
					if(result){
						MessageDialog.showMessageDialog("Model has been saved.");
					} else {
						String message = "Model could not be saved.\n";					
						MessageDialog.showMessageDialog(message);
					}
				}
	
				public void onFailure(Throwable caught) {
					MessageDialog.showMessageDialog(caught.getMessage());
				}
	        });
		}
	}	
	
	public void deleteModel() {
		if (modelManager.getActiveModel() != null) {
			String modelID = modelManager.getActiveModel().getModelTitle();
			RPCService.Util.getInstance().deleteModelByKey(userManager.getCurrentUser().getUser(), modelID, new AsyncCallback<Boolean>() {
				public void onSuccess(Boolean result) {
					if(result){
						MessageDialog.showMessageDialog("Model has been deleted");
					} else {
						MessageDialog.showMessageDialog("Unable to delete model");
					}
				}
				public void onFailure(Throwable caught) {
					MessageDialog.showMessageDialog(caught.getMessage());
				}
			});
		}
		modelManager.setActiveModel(null, false);
	}
	
	public static void checkModel(ModelManager modelManager) {
		String currentText = modelManager.getCurrentText();
		if (currentText != null && currentText.length() > 0) {
			RPCService.Util.getInstance().checkModel(currentText, new AsyncCallback<Boolean>() {
				public void onSuccess(Boolean result) {
					if(result){
						MessageDialog.showMessageDialog(result.toString());
					}
					else {
						MessageDialog.showMessageDialog(result.toString());
					}
				}
				public void onFailure(Throwable caught) {
					MessageDialog.showMessageDialog(caught.getMessage());
				}
			});
		}
	}
	
	public static void saveQuery(QueryInfo query, UserManager userManager){
		RPCService.Util.getInstance().saveQuery(userManager.getCurrentUser().getUser(), query, new AsyncCallback<Boolean>() {
				public void onSuccess(Boolean result) {
					if (result) {
						MessageDialog.showMessageDialog("Query saved successfully");
					} else {
						MessageDialog.showMessageDialog("Error could not save query");
					}
				}
				public void onFailure(Throwable caught) {
					MessageDialog.showMessageDialog("Unable to save query ... reason:\n " + caught.getMessage());
				}
			});
	}

	public static void saveEvidence(EvidenceInfo evidence, UserManager userManager){
		RPCService.Util.getInstance().saveEvidence(userManager.getCurrentUser().getUser(), evidence, new AsyncCallback<Boolean>() {
			public void onSuccess(Boolean result) {
				if (result) {
					MessageDialog.showMessageDialog("Evidence was saved");
				} else {
					MessageDialog.showMessageDialog("Error could not save Evidence");
				}
			}
			public void onFailure(Throwable caught) {
				MessageDialog.showMessageDialog("Unable to save evidence Reason:\n " + caught.getMessage());
			}
		});
	}	
	
	public static void deleteUser(final String userName){
		RPCService.Util.getInstance().deleteUser(userName,  new AsyncCallback<Boolean>() {
			public void onSuccess(Boolean returned) {
				if (returned) {
					MessageDialog.showMessageDialog("User was deleted");
				} else {
					MessageDialog.showMessageDialog("Error could not delete user");
				}
			}
			public void onFailure(Throwable caught) {
				MessageDialog.showMessageDialog(caught.getMessage());
			}
		});
	}
	
	public static void saveUser(final UserInfo user){
		RPCService.Util.getInstance().saveUser(user,  new AsyncCallback<Boolean>() {
			public void onSuccess(Boolean returned) {
				if (returned) {
					MessageDialog.showMessageDialog("User info saved");
				} else {
					MessageDialog.showMessageDialog("Error could not save user info");
				}
			}
			public void onFailure(Throwable caught) {
				MessageDialog.showMessageDialog(caught.getMessage());
			}
		});
	}
}
